//@ts-nocheck
import {CLASS_COMPONENT, ELEMENT, FUNCTION_COMPONENT, TEXT} from './constants';
import {onlyOne, setProps, flatten} from './util';

export function createDOM(element){
  element = onlyOne(element); // emmm todo
  const {$$typeof} = element;
  let dom = null;
  if(!$$typeof){ // element 是一个字符串或则数字 ReactDOM.render(123,document.getElementById('root'))
    dom = document.createTextNode(element);
  }else if($$typeof === TEXT){
    dom = document.createTextNode(element.content);
  }else if($$typeof === ELEMENT){
    dom = createNativeDOM(element);
  }else if($$typeof === FUNCTION_COMPONENT){
    dom = createFunctionComponentDOM(element);
  }else if($$typeof === CLASS_COMPONENT){
    dom = createClassComponentDOM(element);
  }
  element.dom = dom; // 不管什么类型元素 都让它的dom属性指向他创建出来的真实dom元素 compareTwoElements方法中会用到
  return dom;
}

/** 创建函数组件对应的真实DOM对象*/
function createFunctionComponentDOM(element){
  const {type, props} = element;
  const renderElement = type(props);
  element.renderElement = renderElement; // 指向上一次渲染的虚拟DOM节点 用于dom diff
  const newDOM = createDOM(renderElement);
  // 虚拟DOM的dom属性指向它创建出来的真实dom
  renderElement.dom = newDOM;
  return newDOM
}

/** 创建类组件对应的真实DOM对象*/
function createClassComponentDOM(element){
  const {type, props} = element;
  const componentInstance = new type(props);
  element.componentInstance = componentInstance;
  const renderElement = componentInstance.render();
  componentInstance.renderElement = renderElement; // 指向上一次渲染的虚拟DOM节点 用于dom diff 参看component.tsx中的forceUpdate方法
  const newDOM = createDOM(renderElement);
  // 虚拟DOM的dom属性指向它创建出来的真实dom
  renderElement.dom = newDOM;
  return newDOM
}


function createNativeDOM(element){
  const {type, props} = element;
  const dom = document.createElement(type); // 真实的BUTTON DOM对象
  // 1. 创建此虚拟DOM节点的子节点
  createDOMChildren(dom, element.props.children);
  setProps(dom, props);
  // 2. 给此DOM元素添加属性
  return dom;
}

function createDOMChildren(parentNode, children){
  children && /*children.flat(Infinity)*//**←这个是深克隆的*/flatten(children).forEach((child, index) => {

    //child是虚拟dom,我们会在虚拟DOM上加一个属性_mountIndex,指向此DOM节点在父节点中的索引
    //在后面我们做dom-diff的时候有用
    child._mountINdex = index;

    const childDOM = createDOM(child); // 创建此虚拟DOM节点的真实DOM元素
    parentNode.appendChild(childDOM);
  });
}

export function compareTwoElements(oldRenderElement, newRenderElement) {
  oldRenderElement = onlyOne(oldRenderElement);
  newRenderElement = onlyOne(newRenderElement);
  const currentDOM = oldRenderElement.dom;
  let currentElement;
  if (newRenderElement == null) { // return ({this.state.number==0?<div></div>:null})
    currentDOM.parentNode.removeChild(currentDOM);
    currentElement = null;
  } else if (oldRenderElement.type != newRenderElement.type) {//span(字符串) div(字符串)... function(函数本身) class(类本身)   文本节点的话 比较特殊 它的type = TEXT = Symbol.for('TEXT')
    const newDom = createDOM(newRenderElement);
    currentDOM.parentNode.replaceChild(newDom, currentDOM);
    currentElement = newRenderElement;
  } else {
    //TODO
    //这里我们先这样写 后面进行更详细的dom diff
    const newDom = createDOM(newRenderElement);
    currentDOM.parentNode.replaceChild(newDom, currentDOM);
    currentElement = newRenderElement;
  }
  return currentElement;
}

export function ReactElement($$typeof, type, key, ref, props) {
  const element = {
    $$typeof, type, key, ref, props
  };
  return element;
}
